package com.hyny.framework.platform.system.config;

import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;

import com.hyny.framework.platform.system.exception.CommonException;
import com.hyny.framework.platform.system.entity.dto.LoginUserDataPowerDTO;
import com.hyny.framework.platform.system.utils.ExpressionUtils;
import com.hyny.framework.platform.system.utils.SecurityUtil;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.statement.select.*;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.springframework.beans.factory.annotation.Value;

import java.sql.Connection;
import java.util.Iterator;
import java.util.List;

/**
 * MybatisPlus 数据权限拦截器
 */
@Slf4j
public class MybatisPlusDataPowerInnerInterceptor extends JsqlParserSupport implements InnerInterceptor {

    @Value("${spring.application.name}")
    private String applicationName = "";

    @Override
    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {

        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
        MappedStatement ms = mpSh.mappedStatement();
        SqlCommandType sct = ms.getSqlCommandType();
        //过滤出查询接口
        if (sct == SqlCommandType.SELECT) {

            PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
            mpBs.sql(parserMulti(mpBs.sql(), null));
        }


    }

    /**
     * 查询
     */
    @Override
    protected void processSelect(Select select, int index, String sql, Object obj) {
        SelectBody selectBody = select.getSelectBody();
        PlainSelect plainSelect = (PlainSelect) selectBody;

        // 获取表名/别名(如果是关联查询是取第一个join左侧的表名/别名)
        String tableName = ExpressionUtils.getTableName(plainSelect);

        String aliasName = ExpressionUtils.getAliasName(plainSelect);

        // 构建用户权限控制条件
        this.buildUserPermissionSql(plainSelect, sql, tableName, aliasName);
    }

    /**
     * 构建用户权限控制条件
     * 思路:
     * 1、获取当前用户信息,再通过用户信息获取到角色对应的数据权限集合
     * 2、通过获取到的该用户所有的数据权限集合过滤出本次请求的数据权限
     * 3、将本次请求的数据权限拼接到已有的SQL中
     *
     * @param plainSelect 用于解析SQL的类
     * @param tableName   表名/别名(join查询左侧表名)
     */
    private void buildUserPermissionSql(PlainSelect plainSelect, String sql, String tableName, String aliasName) {


        List<SelectItem> selectItems = plainSelect.getSelectItems();
        boolean isUpdate = false;
        boolean isAlias = false;
        if (aliasName != null) {
            isAlias = true;
        }
        for (SelectItem si : selectItems
        ) {
            if (si instanceof SelectExpressionItem) {
                SelectExpressionItem selectExpressionItem = (SelectExpressionItem) si;
                String fieldName = selectExpressionItem.getExpression().toString();
                int dotIndex = fieldName.indexOf(".");
                if (dotIndex != 0) {
                    fieldName = fieldName.substring(dotIndex + 1, fieldName.length());
                }


                try {
                    LoginUserDataPowerDTO loginUserDataPowerDTOS = SecurityUtil.getLoginUser().getDataPowerDTOs();
                    if (loginUserDataPowerDTOS != null) {
                        Iterator<String> iterator = loginUserDataPowerDTOS.getDataPowerList().keySet().iterator();
                        while (iterator.hasNext()) {
                            String key = iterator.next();
                            List<String> values = loginUserDataPowerDTOS.getDataPowerList().get(key);
                            ExpressionUtils.appendExpression(plainSelect, ExpressionUtils.buildInSql(isAlias ? aliasName : tableName, fieldName, values));

                        }
                    }

                } catch (Exception e) {

                    if (e instanceof CommonException) {
                        if (((CommonException) e).getExceptionMessage().equals("获取用户信息异常")) {
                            return;
                        }
                    }

                    throw new RuntimeException(e);
                }


            }
        }

        if (isUpdate) {
            log.info("[增加数据权限]原始SQL:[{}]", sql);
            log.info("[增加数据权限]处理后SQL:[{}]", plainSelect);
        }
    }


}
